编写 ls -l: stat
ls -l 列出一系列的文件信息
$ man -k file | grep -i status
找到 stat
用 stat 得到文件信息
磁盘上的文件有很多属性, 比如大小, 所有者ID. 如果需要得到文件属性, 进程可以定义一个结构 struct stat, 然后调用 stat, 告诉内核把文件属性存放到这个结构中
Stat | |
---|---|
目标 | 得到文件的属性 |
头文件 | #include <sys/stat.h> |
原型 | int result = stat(char *fname, struct stat *bufp) |
参数 | fname 文件名, bufp 指向buffer的指针 |
返回值 | -1 error, 0 success |
下面代码展示了如何用 stat 来得到文件的大小
#include <stdio.h> #include <sys/stat.h> int main() { struct stat infobuf; if (stat("/etc/passwd", &infobuf) == -1) perror("/etc/passwd"); else printf("The size of /etc/passwd is %ld\n", infobuf.st_size); return 0; }
stat 把文件的信息复制到结构 infobuf 中, 程序从成员变量 st_size 中读到文件大小.
stat 提供的其他信息
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* I-node 节点 */ mode_t st_mode; /* 文件类型和许可权限 */ nlink_t st_nlink; /* 硬链接数目 */ off_t st_size; /* 文件字节数 */ uid_t st_uid; /* 文件所有者id */ gid_t st_gid; /* 文件所属组id */ time_t st_mtime; /* 文件最后修改时间 */ time_t st_atime; /* 文件最后访问时间 */ time_t st_ctime; /* 文件属性最后修改时间 */ // ... }
下面的例子 fileinfo.c 得到以上属性, 并显示出来
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> void show_stat_info(char *, struct stat *); int main( int argc, char **argv ) { struct stat info; if (argc > 1){ if (stat(argv[1], &info) != -1){ show_stat_info(argv[1], &info); }else{ perror(argv[1]); } } return 0; } void show_stat_info(char *fname, struct stat *buf) { printf(" mode: %o\n", buf->st_mode); printf(" links: %ld\n", buf->st_nlink); printf(" user: %d\n", buf->st_uid); printf(" group: %d\n", buf->st_gid); printf(" size: %ld\n", buf->st_size); printf(" modtime: %ld\n", buf->st_mtime); printf(" name: %s\n", fname); }
将模式字段转换成字符
st_mode 是一个 16 位的二进制数, 文件权限类型和权限被编码在这个数中 详情查看 Unix 文件权限
使用掩码来解码得到文件类型
文件类型在模式字段的第一个字节的前四位, 可以通过掩码来将其他部分置0, 从而 得到文件类型的值.
在 <sys/stat.h> 中有以下定义
#define S_IFMT 0170000 /* type of file */ #define S_IFREG 0100000 /* regular */ #define S_IFDIR 0040000 /* directory */ #define S_IFBLK 0060000 /* block special */ #define S_IFCHR 0020000 /* character special */ #define S_IFIFO 0010000 /* fifo */ #define S_IFLNK 0120000 /* symbolic link */ #define S_IFSOCK 0140000 /* socket */
S_IFMT 是一个掩码, 它的值是 0170000, 可以用来过滤出前四位表示的文件类型. S_IFREG 代表普通文件, 值是0100000, S_IFDIR 代表目录文件, 值是 0040000
if ((info.st_mode & 0170000) == 0040000) printf("this is a directory");
更简单的方法是用 <sys/stat.h> 中的宏来替代上述代码:
#define S_ISFIFO(m) (((m) & (0170000)) == (0010000)) #define S_ISDIR(m) (((m) & (0170000)) == (0040000)) #define S_ISCHR(m) (((m) & (0170000)) == (0040000)) #define S_ISBLK(m) (((m) & (0170000)) == (0020000)) #define S_ISREG(m) (((m) & (0170000)) == (0100000))
if (S_ISDIR(info.st_mode)) printf("this is a directory");
解码得到许可权限
模式字段的最低9位是许可权限, 它标识了文件所有者, 组用户和其他用户的读写和执行 权限
void mode_to_letters(int mode, char str[]) { strcpy(str, "----------"); if(S_ISDIR(mode)) str[0] = 'd'; if(S_ISCHR(mode)) str[0] = 'c'; if(S_ISBLK(mode)) str[0] = 'b'; if (mode && S_IRUSR) str[1] = 'r'; if (mode && S_IWUSR) str[2] = 'w'; if (mode && S_IXUSR) str[3] = 'x'; if (mode && S_IRGRP) str[4] = 'r'; if (mode && S_IWGRP) str[5] = 'w'; if (mode && S_IXGRP) str[6] = 'x'; if (mode && S_IROTH) str[7] = 'r'; if (mode && S_IWOTH) str[8] = 'w'; if (mode && S_IXOTH) str[9] = 'x'; }
将用户/组 ID 转换成字符串
库函数 getpwuid 可以访问用户信息, getpwuid 需要 UID 作为参数, 返回一个指向 struct passwd 的指针, 这个接口定义在 /usr/include/pwd.h 中:
struct passwd { char * pw_name; /* Username */ char * pw_passwd; /* Password */ _uid_t pwd_uid; /* User ID */ _gid_t pw_gid; /* Group ID */ char * pw_gecos; /* Real name */ char * pw_dir; /* Home directory */ char * pw_shell; /* Shell program */ };
getgrgid 函数可以返回 一个指向 struct group的指针, 这个接口定义在 /usr/include/grp.h 中:
struct group { char *gr_name; /* group name */ char *gr_passwd; /* group password */ gid_t gr_gid; /* group ID */ char **gr_mem; /* group members */ };
编写ls2.c
#include <stdio.h> #include <sys/types.h> #include <dirent.h> #include <sys/stat.h> void do_ls(char []); void dostat(char *); void show_file_info(char *, struct stat *); void mode_to_letters(int, char []); char *uid_to_name(uid_t); char *gid_to_name(gid_t); int main( int argc, char **argv ) { if (argc == 1) do_ls("."); else while (--argc){ printf("%s:\n", *++argv); do_ls(*argv); } return 0; } void do_ls(char dirname[]) { DIR *dir_ptr; struct dirent *direntp; if ((dir_ptr = opendir(dirname)) == NULL) fprintf(stderr, "ls2: cannot open %s\n", dirname); else { while((direntp = readdir(dir_ptr))) dostat(direntp->d_name); closedir(dir_ptr); } } void dostat(char *filename) { struct stat info; if (stat(filename, &info) == -1) perror(filename); else show_file_info(filename, &info); } void show_file_info(char *filename, struct stat * info_p) { char modestr[11]; mode_to_letters(info_p->st_mode, modestr); printf("%s", modestr); printf("%4d", (int) info_p->st_nlink); printf("%-8s", uid_to_name(info_p->st_uid)); printf("%-8s", gid_to_name(info_p->st_gid)); printf("%8ld", (long)info_p->st_size); printf("%.12s", 4 + ctime(&info_p->st_mtime)); printf("%s\n", filename); } void mode_to_letters(int mode, char str[]) { strcpy(str, "----------"); if (S_ISDIR(mode)) str[0] = 'd'; if (S_ISCHR(mode)) str[0] = 'c'; if (S_ISBLK(mode)) str[0] = 'b'; if (mode & S_IRUSR) str[1] = 'r'; if (mode & S_IWUSR) str[2] = 'w'; if (mode & S_IXUSR) str[3] = 'x'; if (mode & S_IRGRP) str[4] = 'r'; if (mode & S_IWGRP) str[5] = 'w'; if (mode & S_IXGRP) str[6] = 'x'; if (mode & S_IROTH) str[7] = 'r'; if (mode & S_IWOTH) str[8] = 'w'; if (mode & S_IXOTH) str[9] = 'x'; } #include <pwd.h> char *uid_to_name(uid_t uid) { struct passwd *pw_ptr; static char numstr[10]; if ((pw_ptr = getpwuid(uid)) == NULL){ sprintf(numstr, "%d", uid); return numstr; }else return pw_ptr->pw_name; } #include <grp.h> char *gid_to_name(gid_t gid) { struct group *grp_ptr; static char numstr[10]; if ((grp_ptr = getgrgid(gid)) == NULL){ sprintf(numstr, "%d", gid); return numstr; }else return grp_ptr->gr_name; }